package com.dtp.adapter.common;

import com.dtp.common.ApplicationContextHolder;
import com.dtp.common.entity.NotifyPlatform;
import com.dtp.common.entity.ThreadPoolStats;
import com.dtp.common.entity.TpExecutorProps;
import com.dtp.common.entity.TpMainFields;
import com.dtp.common.properties.DtpProperties;
import com.dtp.common.util.StreamUtil;
import com.dtp.core.convert.ExecutorConverter;
import com.dtp.core.convert.MetricsConverter;
import com.dtp.core.notify.manager.AlarmManager;
import com.dtp.core.notify.manager.NoticeManager;
import com.dtp.core.support.ExecutorWrapper;
import com.dtp.core.thread.ExecutorAdapter;
import com.github.dadiyang.equator.Equator;
import com.github.dadiyang.equator.FieldInfo;
import com.github.dadiyang.equator.GetterBaseEquator;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadPoolExecutor;

import static com.dtp.common.constant.DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE;
import static com.dtp.core.notify.manager.NotifyHelper.updateNotifyInfo;
import static java.util.stream.Collectors.toList;

/**
 * DtpAdapter抽象类
 *
 * @author yanhom
 * @author dragon-zhang
 * @since 1.0.6
 *
 * @remark muse
 */
@Slf4j
public abstract class AbstractDtpAdapter implements DtpAdapter, GenericApplicationListener {
    private static final Equator EQUATOR = new GetterBaseEquator();
    protected final Map<String, ExecutorWrapper> executors = Maps.newHashMap(); // 线程池缓存

    /** 判断是否是ApplicationReadyEvent类型的事件，否则返回false */
    @Override
    public boolean supportsEventType(ResolvableType resolvableType) {
        Class<?> type = resolvableType.getRawClass();
        if (type != null) return ApplicationReadyEvent.class.isAssignableFrom(type);
        return false;
    }

    /** 接受应用事件响应 */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent) {
            try {
                DtpProperties dtpProperties = ApplicationContextHolder.getBean(DtpProperties.class);
                initialize();
                refresh(dtpProperties); // 执行线程池的刷新操作
            } catch (Exception e) {
                log.error("Init third party thread pool failed.", e);
            }
        }
    }

    /** 获得多个线程池状态（ThreadPoolStats） */
    @Override
    public List<ThreadPoolStats> getMultiPoolStats() {
        val executorWrappers = getExecutorWrappers();
        if (MapUtils.isEmpty(executorWrappers)) return Collections.emptyList();

        List<ThreadPoolStats> threadPoolStats = Lists.newArrayList();
        // 将ExecutorWrapper转换成ThreadPoolStats，并保存到threadPoolStats列表中
        executorWrappers.forEach((k, v) -> threadPoolStats.add(MetricsConverter.convert(v)));
        return threadPoolStats;
    }

    /** 初始化警报机制 */
    public void initNotifyItems(String poolName, ExecutorWrapper executorWrapper) {
        AlarmManager.initAlarm(poolName, executorWrapper.getNotifyItems());
    }

    /** 刷新线程池 */
    public void refresh(String name, List<TpExecutorProps> propsList, List<NotifyPlatform> platforms) {
        // 如果没有线程池列表，或者没有线程池基本属性列表，则直接返回
        val executorWrappers = getExecutorWrappers();
        if (CollectionUtils.isEmpty(propsList) || MapUtils.isEmpty(executorWrappers)) return;

        val tmpMap = StreamUtil.toMap(propsList, TpExecutorProps::getThreadPoolName); // 获得线程池名称与线程池基本属性对应Map
        executorWrappers.forEach((k, v) -> refresh(name, v, platforms, tmpMap.get(k))); // 执行线程池的刷新操作
    }

    /** 线程池的刷新操作 */
    public void refresh(String name, ExecutorWrapper executorWrapper, List<NotifyPlatform> platforms, TpExecutorProps props) {
        if (Objects.isNull(props) || Objects.isNull(executorWrapper) || containsInvalidParams(props, log))
            return;

        TpMainFields oldFields = getTpMainFields(executorWrapper, props);
        doRefresh(executorWrapper, platforms, props); // 刷新操作
        TpMainFields newFields = getTpMainFields(executorWrapper, props);
        if (oldFields.equals(newFields)) {
            log.debug("DynamicTp adapter refresh, main properties of [{}] have not changed.", executorWrapper.getThreadPoolName());
            return;
        }

        List<FieldInfo> diffFields = EQUATOR.getDiffFields(oldFields, newFields);
        List<String> diffKeys = diffFields.stream().map(FieldInfo::getFieldName).collect(toList());
        NoticeManager.doNoticeAsync(executorWrapper, oldFields, diffKeys);
        log.info("DynamicTp {} adapter, [{}] refreshed end, changed keys: {}, corePoolSize: [{}], "
                        + "maxPoolSize: [{}], keepAliveTime: [{}]",
                name, executorWrapper.getThreadPoolName(), diffKeys,
                String.format(PROPERTIES_CHANGE_SHOW_STYLE, oldFields.getCorePoolSize(), newFields.getCorePoolSize()),
                String.format(PROPERTIES_CHANGE_SHOW_STYLE, oldFields.getMaxPoolSize(), newFields.getMaxPoolSize()),
                String.format(PROPERTIES_CHANGE_SHOW_STYLE, oldFields.getKeepAliveTime(), newFields.getKeepAliveTime()));
    }

    /** 真正执行线程池的刷新操作 */
    protected void doRefresh(ExecutorWrapper executorWrapper, List<NotifyPlatform> platforms, TpExecutorProps props) {
        val executor = executorWrapper.getExecutor();
        doRefreshPoolSize(executor, props); // 刷新核心和最大线程池数
        if (!Objects.equals(executor.getKeepAliveTime(props.getUnit()), props.getKeepAliveTime()))
            executor.setKeepAliveTime(props.getKeepAliveTime(), props.getUnit());

        if (StringUtils.isNotBlank(props.getThreadPoolAliasName()))
            executorWrapper.setThreadPoolAliasName(props.getThreadPoolAliasName());

        updateNotifyInfo(executorWrapper, props, platforms); // 更新通知项
    }

    /** 刷新核心线程池数（corePoolSize）与最大线程池数（maximumPoolSize），此方法逻辑与DtpRegistry.doRefreshPoolSize(...)一样 */
    private void doRefreshPoolSize(ExecutorAdapter<?> executor, TpExecutorProps props) {
        if (props.getMaximumPoolSize() >= executor.getMaximumPoolSize()) {
            if (!Objects.equals(props.getMaximumPoolSize(), executor.getMaximumPoolSize()))
                executor.setMaximumPoolSize(props.getMaximumPoolSize());
            if (!Objects.equals(props.getCorePoolSize(), executor.getCorePoolSize()))
                executor.setCorePoolSize(props.getCorePoolSize());
            return;
        }

        if (!Objects.equals(props.getCorePoolSize(), executor.getCorePoolSize()))
            executor.setCorePoolSize(props.getCorePoolSize());
        if (!Objects.equals(props.getMaximumPoolSize(), executor.getMaximumPoolSize()))
            executor.setMaximumPoolSize(props.getMaximumPoolSize());
    }

    /** 获得动态线程池主要属性（TpMainFields） */
    protected TpMainFields getTpMainFields(ExecutorWrapper executorWrapper, TpExecutorProps props) {
        return ExecutorConverter.convert(executorWrapper);
    }

    @Override
    public Map<String, ExecutorWrapper> getExecutorWrappers() {return executors;}
    protected void initialize() {}
    public void register(String poolName, ThreadPoolExecutor threadPoolExecutor) {}
}
